package uk.ac.aber.paws.server.stats;

import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.logging.Level;

import uk.ac.aber.paws.server.config.ConfigManager;
import uk.ac.aber.paws.server.core.PAWSServer;
import uk.ac.aber.paws.server.db.DBLogger;
import uk.ac.aber.paws.server.ldap.LDAPQuery;

/**
 * @author Rob Johnson, Aberystwyth University
 * @email rob.johnson@aber.ac.uk
 * @date 21st May 2012
 * 
 * Archiving.java
 * 
 * Perform statisitcs archiving operations within the database
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class Archiving {

	/**
	 * Refreshes the top level OU stats, grouped by the date. There is typically only one top 
	 * level OU per domain.
	 */
	public static void initiateOrgSavingsRefresh() {
		
		// get the root ous
		Vector<Hashtable<String,Object>> ouList = (new DBLogger()).getRootOUs();
		
		int period = Integer.parseInt(ConfigManager.getProperty("app.archiving.orgrefreshmonths"));
		
		Calendar toDate = getCalendarObj(Calendar.getInstance(), true);
		Calendar fromDate = ((Calendar)toDate.clone());
		fromDate.add(Calendar.MONTH, period*-1);
		
		for (int i=0;i<ouList.size();i++) {
			// call the database call to refresh the stats for the required period
			(new DBLogger()).refreshOrgSavings(((String)ouList.get(i).get("StatsRootOU")).toUpperCase(), fromDate, toDate);
		}
	}
	
	/**
	 * Purges the power event logs within the database
	 */
	public static void purgeDatabase() {
	
		int period = Integer.parseInt(ConfigManager.getProperty("app.archiving.purgepowereventslogsperiod"));
		
		Calendar cutOffDate = getCalendarObj(Calendar.getInstance(), true);
		cutOffDate.add(Calendar.MONTH, period*-1);
		
		(new DBLogger()).purgePowerEventLogs(cutOffDate);
	}
	
	/**
	 * Refreshes the saving statistic for the specified date. This is typically called to repetitively update the daily
	 * stats for the current day.
	 * @param date The day to be updated
	 */
	public static void refreshTodayPowerEventLogs(Calendar date) {
	
		// get the root ou list form the database
		Vector<Hashtable<String,Object>> ouList = (new DBLogger()).getRootOUs();
	
		Hashtable<String, Vector<String>> bottomOUs =  LDAPQuery.getAllBottomOUs(ouList);
		
		PAWSServer.LOG_FILE.log(Level.ALL,"Bottom OUs for Stats Generation="+bottomOUs.toString());
		
		Calendar fromDate = (Calendar)date.clone();
		Calendar toDate = (Calendar)date.clone();
		
		// for each bottom ou - retrieve the sleep information for each computer
		Enumeration<String> keys = bottomOUs.keys();
		
		// for each identified bottom ou
		while (keys.hasMoreElements()) {
		
			String key = keys.nextElement();
				
			Vector<String> computerList = bottomOUs.get(key);
				
			PAWSServer.LOG_FILE.log(Level.ALL,"Updating archive stats for today");
			
			collateStats(computerList,key,getCalendarObj(fromDate,true),getCalendarObj(toDate,false));
		}
	}
	
	public static void initiatePowerLogsArchiveProcess(Calendar date) {
		
		// get the root ou list form the database
		Vector<Hashtable<String,Object>> ouList = (new DBLogger()).getRootOUs();
	
		Hashtable<String, Vector<String>> bottomOUs =  LDAPQuery.getAllBottomOUs(ouList);
		
		PAWSServer.LOG_FILE.log(Level.ALL,"Bottom OUs for Stats Generation="+bottomOUs.toString());
		
		// for each bottom ou - retrieve the sleep information for each computer
		Enumeration<String> keys = bottomOUs.keys();
		
		// for each identified bottom ou
		while (keys.hasMoreElements()) {
		
			String key = keys.nextElement();
			
			Vector<String> computerList = bottomOUs.get(key);
			
			checkForArchives(computerList,key,date);
		}
	}
	
	private static void collateStats(Vector<String> computerList, String ou, Calendar fromDate, Calendar toDate) {
		
		// get the list of computers within the bottom OU
		String computerListStr = "";
		String delim = ",";
		
		for (int j=0;j<computerList.size();j++) {
			computerListStr += computerList.get(j)+delim; 
		}
		
		System.out.println("Updating daily stats for "+ou.toUpperCase());
		System.out.println(computerListStr);
		
		PAWSServer.LOG_FILE.log(Level.ALL,"Updating daily stats for "+ou.toUpperCase());
		
		(new DBLogger()).archiveEventLogs(computerListStr,delim,ou.toUpperCase(),fromDate,toDate);
	
	}

	private static void checkForArchives(Vector<String> computerList, String ou, Calendar date) {
		
		date =  getCalendarObj(date,true);
		Calendar backTrack = (Calendar)date.clone();
		
		int archiveMonths = Integer.parseInt(ConfigManager.getProperty("app.archiving.bottomoustatsrefreshperiod"));
		
		backTrack.add(Calendar.MONTH, archiveMonths*-1);
	
		// rearchive stats if option is set
		if (ConfigManager.getProperty("app.options.rebuildarchive") != null) {
			if (Integer.parseInt(ConfigManager.getProperty("app.options.rebuildarchive")) == 1) {
				
				System.out.println("Stats Archive Refresh Started");
				
				collateStats(computerList,ou,backTrack,date);
				
				System.out.println("Stats Archive Refresh Finished");
			}
		}

		// parse through all archives and check existance of all stats
		while (backTrack.before(date)) {
			
			Calendar toDate = (Calendar)backTrack.clone();
			toDate.add(Calendar.DATE, 1);
			
			// check if archive entry exists, if not repopulate
			
			if ((new DBLogger().validateArchive(ou, backTrack))) {
				System.out.println("Adding stats for "+new Date(backTrack.getTimeInMillis()).toString());
				collateStats(computerList,ou,backTrack,toDate);
			}
			else {
				System.out.println(ou+" on "+backTrack+" already exists");
			}
			
			backTrack.add(Calendar.DATE, 1);
		}
		
		
	}

	public static boolean isToday(Calendar sourceObj) {
		
		Calendar today = Calendar.getInstance();
		
		if (today.get(Calendar.YEAR) == sourceObj.get(Calendar.YEAR)) {
			if (today.get(Calendar.MONTH) == sourceObj.get(Calendar.MONTH)) {
				if (today.get(Calendar.DAY_OF_MONTH) == sourceObj.get(Calendar.DAY_OF_MONTH)) {
					return true;
				}
			}
		}
		
		return false;
	}
	
	public static Calendar getCalendarObj(Calendar cal, boolean start) {
		
		if (start) {
			cal.set(Calendar.HOUR_OF_DAY, cal.getActualMinimum(Calendar.HOUR_OF_DAY));
			cal.set(Calendar.MINUTE,cal.getActualMinimum(Calendar.MINUTE));
			cal.set(Calendar.SECOND,cal.getActualMinimum(Calendar.SECOND));
			cal.set(Calendar.MILLISECOND,cal.getActualMinimum(Calendar.MILLISECOND));
		}
		else {
			if (!isToday(cal)) {
				cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY));
				cal.set(Calendar.MINUTE,cal.getActualMaximum(Calendar.MINUTE));
				cal.set(Calendar.SECOND,cal.getActualMaximum(Calendar.SECOND));
				cal.set(Calendar.MILLISECOND,cal.getActualMaximum(Calendar.MILLISECOND));
			}
			else {
				Calendar now = Calendar.getInstance();
				cal.set(Calendar.HOUR_OF_DAY, now.get(Calendar.HOUR_OF_DAY));
				cal.set(Calendar.MINUTE,now.get(Calendar.MINUTE));
				cal.set(Calendar.SECOND,now.get(Calendar.SECOND));
				cal.set(Calendar.MILLISECOND,now.get(Calendar.MILLISECOND));
			}
		}
		
		return cal;
	}
}
